package mirrormonkey.util.annotations.parsing.memberlevel;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;

import mirrormonkey.util.annotations.control.AnnotationOverride;
import mirrormonkey.util.annotations.control.IRClass;
import mirrormonkey.util.annotations.parsing.AnnotationParser;
import mirrormonkey.util.annotations.parsing.ClassIR;
import mirrormonkey.util.annotations.parsing.FieldIR;
import mirrormonkey.util.annotations.parsing.MemberIR;
import mirrormonkey.util.annotations.parsing.MethodIR;

import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 * Contains test cases concerning the instantiation and management of
 * <tt>AnnotationIRs</tt> where there is no class hierarchy or inheritance.
 * 
 * @author Philipp Christian Loewner
 * 
 */
public class FlatMemberIRManagementTest {

	public static AnnotationParser parser;

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		parser = new AnnotationParser();
	}

	public static class ContainingFieldIR {

		@IRClass(FieldIR.class)
		public Object field;

	}

	/**
	 * Tests if <tt>FieldIRs</tt> are created for fields.
	 */
	@Test
	public void testFieldAcceptAnnotationIR() {
		ClassIR ir = parser.parseClass(ContainingFieldIR.class, null);
		Assert.assertEquals(1, ir.memberIRs.get(Field.class).size());
		Assert.assertEquals(FieldIR.class, ir.memberIRs.get(Field.class)
				.iterator().next().getClass());
	}

	public static class ContainingMethodIR {

		@IRClass(MethodIR.class)
		public void method() {
		}

	}

	/**
	 * Tests if <tt>MethodIRs</tt> are created for methods.
	 */
	@Test
	public void testMethodAcceptAnnotationIR() {
		ClassIR ir = parser.parseClass(ContainingMethodIR.class, null);
		Assert.assertEquals(1, ir.memberIRs.get(Method.class).size());
		Assert.assertEquals(MethodIR.class, ir.memberIRs.get(Method.class)
				.iterator().next().getClass());
	}

	public static class LegalMemberIR extends MemberIR {

		public Member member;

		public LegalMemberIR(AnnotationParser parser, Field element) {
			super(parser, element);
			member = element;
		}

		public LegalMemberIR(FieldIR previous, Field element) {
			super(previous, element);
			member = element;
		}

		public LegalMemberIR(LegalMemberIR previous, Field element) {
			super(previous, element);
			member = element;
		}

		public LegalMemberIR(AnnotationParser parser, Method element) {
			super(parser, element);
			member = element;
		}

		public LegalMemberIR(MethodIR previous, Method element) {
			super(previous, element);
			member = element;
		}

		public LegalMemberIR(LegalMemberIR previous, Method element) {
			super(previous, element);
			member = element;
		}

		@Override
		public boolean matches(Member member) {
			return member.equals(member);
		}

		@Override
		public void parseField(Field f) {
			member = f;
			parseAnnotatedElement(f);
		}

		@Override
		public void parseMethod(Method m) {
			member = m;
			parseAnnotatedElement(m);
		}

		@Override
		public void parseConstructor(Constructor<?> constr) {
		}

		@Override
		public Class<?>[] getListenMemberClasses() {
			return new Class<?>[] { Constructor.class, Field.class,
					Method.class };
		}

	}

	public static class ContainingExtendedFieldIR {

		@IRClass(LegalMemberIR.class)
		public Object field;

	}

	/**
	 * Tests if it is possible to use custom IR classes on fields.
	 */
	@Test
	public void testExtendFieldIR() {
		ClassIR ir = parser.parseClass(ContainingExtendedFieldIR.class, null);
		Assert.assertEquals(1, ir.memberIRs.get(Field.class).size());
		Assert.assertEquals(LegalMemberIR.class, ir.memberIRs.get(Field.class)
				.iterator().next().getClass());
	}

	public static class ContainingExtendedMethodIR {
		@IRClass(LegalMemberIR.class)
		public Object field() {
			return null;
		}
	}

	/**
	 * Tests if it is possible to use custom IR classes on methods.
	 */
	@Test
	public void testExtendMethodIR() {
		ClassIR ir = parser.parseClass(ContainingExtendedMethodIR.class, null);
		Assert.assertEquals(1, ir.memberIRs.get(Method.class).size());
		Assert.assertEquals(LegalMemberIR.class, ir.memberIRs.get(Method.class)
				.iterator().next().getClass());
	}

	public static class ContainingNonAnnotatedField {

		@AnnotationOverride
		public Object field;

		@AnnotationOverride
		public void method() {
		}

	}

	/**
	 * Tests if <tt>AnnotationOverride</tt> does not create any IRs on fields if
	 * there were none already present.
	 */
	@Test
	public void testEmptyAnnotationIRIgnore() {
		ClassIR ir = parser.parseClass(ContainingNonAnnotatedField.class, null);
		for (Class<?> i : new Class<?>[] { Field.class, Method.class }) {
			Assert.assertTrue(ir.memberIRs.get(i).isEmpty());
		}
	}

	public static class ContainingIllegalField {

		@IRClass(Object.class)
		public Object field;

	}

	/**
	 * Tests if <tt>AnnotationParser</tt> throws an exception an
	 * <tt>IRClass</tt> uses a class that does not inherit from
	 * <tt>AnnotationIR</tt> at all.
	 */
	@Test(expected = RuntimeException.class)
	public void testRejectIllegalIRClass() {
		parser.parseClass(ContainingIllegalField.class, null);
	}

	public static class IllegalFirstIR extends MemberIR {

		public IllegalFirstIR(MemberIR previous, Member member) {
			super(previous, (AnnotatedElement) member);
		}

		@Override
		public boolean matches(Member member) {
			return false;
		}

		@Override
		public void parseField(Field f) {
		}

		@Override
		public void parseMethod(Method m) {
		}

		@Override
		public void parseConstructor(Constructor<?> constr) {
		}

		@Override
		public Class<?>[] getListenMemberClasses() {
			return null;
		}

	}

	public static class ContainingIllegalFirstIR {

		@IRClass(IllegalFirstIR.class)
		public Object field;

	}

	/**
	 * Tests if using an IR class that does not define a constructor for
	 * <tt>AnnotationParser</tt> will provoke an exception.
	 */
	@Test(expected = RuntimeException.class)
	public void testInvalidFirstIRClass() {
		parser.parseClass(ContainingIllegalFirstIR.class, null);
	}

}
